﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using SdmxMl.Common;
using SdmxMl.Structure;
using SdmxMl.GenericMetadata;

namespace SdmxMl.Manager
{
    

    /// <summary>
    // Urn manager helps to manipulate artefacts references as
    // Unique identification of SDMX objects.
    /// </summary>
    public class UrnManager
    {
        // Package name must have correspondance to SdmxArtefactType enumeration
        public static string[] _arrUrnSdmxPackage = {
            "?",
            "base",
            "datastructure",      // No documentation found is it correct ???
            "metadatastructure", // No documentation found is it correct ???
            "categoryscheme",
            "categorisation",
            "codelist",
            "codelist",
            "conceptscheme",
            "metadatastructure",
            "dataStructure",
            "structuresets",
            "reportingtaxonomy",
            "process",
            "?",
            "metadataset",
        };

        // Package name must have correspondance to SdmxArtefactType enumeration
        public static string[] _arrUrnArtefact = {
            "? ",
            ".OrganisationScheme",
            ".Dataflow",
            ".Metadataflow",
            ".CategoryScheme",
            ".Categorisation",
            ".CodeList",
            ".HierarchicalCodelist",
            ".ConceptScheme",
            ".MetadataStructureDefinition",
            ".DataStructure",
            ".StructureSets",
            ".ReportingTaxonomy",
            ".Process",
            "? ",
            ".MetadataSet",
        };

        private static string _urnBase;
        private static string _urnBaseConfigTag = "urnBase";
        public static string UrnBase
        {
            get
            {
                if (_urnBase == null)
                {
                    
                    if (ConfigurationManager.AppSettings[_urnBaseConfigTag] == null)
                        throw new ApplicationException("URN base configuration not defined in configuration file!");
                    _urnBase = ConfigurationManager.AppSettings[_urnBaseConfigTag].ToString().ToLower();
                }
                return _urnBase;
            }
        }

       #region private C'tor

        private UrnManager()
        {
        }

        #endregion

        #region FullId to URN
        public static string FullIdToUrn(SdmxArtefactType arType, string fullId)
        {
            return FullIdToUrn(arType.ToString(), fullId, null, null);
        }

        public static string FullIdToUrn(string arType, string fullId)
        {
            return FullIdToUrn(arType, fullId, null, null);
        }

        public static string FullIdToUrn(SdmxArtefactType arType, string fullId, object sub, object subParent)
        {
            return FullIdToUrn(arType.ToString(), fullId, sub, subParent);
        }


        public static string FullIdToUrn(string arType, string fullId, object sub, object subParent)
        {
            StringBuilder sb = new StringBuilder(UrnBase);
            sb.Append('.');

            // From type string
            SdmxArtefactType sType = (SdmxArtefactType)Enum.Parse(typeof(SdmxArtefactType), arType, true);

            // get corresponding urn part
            sb.Append(_arrUrnSdmxPackage[(int)sType]);

            ISdmxHierarchyListItem item = null;
            BaseDimensionType bdt = null;
            MetadataAttributeType meta = null;
            ReportStructureType rpt = null;
            AttributeValueSetType at = null;
            RefForMetaset refMetaSet = null;
            HierarchyType ht = null;
            CodelistRefType clrt = null;

            string ReportId = string.Empty;

            if (sub != null)
            {
                item = sub as ISdmxHierarchyListItem;
                if (item != null)
                {
                    if (item is CodeType)
                        sb.Append(".Code");
                    else if (item is ConceptType)
                        sb.Append(".Concept");
                    else if (item is Category)
                        sb.Append(".Category");
                }

                // DSD element ?
                bdt = sub as BaseDimensionType;
                if (bdt != null)
                {
                    if (bdt is DimensionType || bdt is TimeDimensionType)
                        sb.Append(".Dimension");
                    else if (bdt is CrossSectionalMeasureType)
                        sb.Append(".CrossSectionalMeasure");
                    else if (bdt is AttributeType)
                        sb.Append(".Attribute");
                    else
                        bdt = null;

                }
                // MSD Element
                if (subParent != null)
                {
                    rpt = subParent as ReportStructureType;
                    meta = sub as MetadataAttributeType;
                    if (rpt != null && meta != null)
                        sb.Append(".ReportStructure");
                    else
                        rpt = null;
                }

                // Metadataset row & concept
                refMetaSet = subParent as RefForMetaset;
                at = sub as AttributeValueSetType;
                if (at != null)
                {
                    if (refMetaSet != null)
                        sb.Append(".ReportedAttributeType");
                    else
                        sb.Append(".TargetValues");
                }
                // Hierarchy in hierarchicalcodelist
                ht = sub as HierarchyType;
                clrt = sub as CodelistRefType;
                if (ht != null)
                    sb.Append(".Hierarchy");

                //Codelistref in hierarchicalcodelist
                if (clrt != null)
                    sb.Append(".CodelistRef");
            }
            else
            {
                sb.Append(_arrUrnArtefact[(int)sType]);
            }
            sb.Append('=');
            //AgencyId:Id[version]
            string[] arr = fullId.Split('+');
            if (arr.Length != 3)
                throw new ApplicationException("Invalid FullId received for urn construction!");

            sb.Append(arr[0]).Append(':').Append(arr[1]);
            sb.Append('[').Append(arr[2]).Append(']');

            if (item != null)
            {
                sb.Append(".");
                if (item is Category)
                    sb.Append((item as Category).FullId);
                else
                    sb.Append(item.Id);
            }

            // some kind of dimension in DSD
            if (bdt != null)
                sb.Append(".").Append(bdt.ConceptInfo.Id);

            if (rpt != null)
                sb.Append('.').Append(rpt.Id).Append('.').Append(meta.ConceptInfo.Id);

            // A metadataset row (AttributeValueSetType)
            if (at != null)
            {
                // URI type on a concept or a codelist (Extension for find and replace references for metadataset)
                if (refMetaSet != null)
                    sb.Append('.').Append(refMetaSet.ReportId).Append('.').Append(at.ToString()).Append('.').Append(refMetaSet.ConceptId);
                else
                {
                    // ID given according to TargetRef (only one in SDMX 2.0)
                    sb.Append('.').Append(at.TargetRef);
                    foreach (ComponentValueType cvt in at.TargetValuesTypeList)
                        sb.Append('.').Append(cvt.Value);
                }
            }

            // Hierarchy of a hierarchical list
            if (ht != null)
                sb.Append(".").Append(ht.Id);

            if (clrt != null)
                sb.Append(".").Append(clrt.Alias);


            return sb.ToString();
        }

        #endregion

        public static ArtefactRefTyped UrnToArtefactRefTyped(string urn)
        {
            ArtefactRefTyped art = null;

            UrnParser p = new UrnParser(urn);
            if (p.IsSupported)
                art = new ArtefactRefTyped(p.ArType, p.ArtefactId, p.AgencyId, p.Version);

            return art;
        }

        // Update artefacts created in Harmonization phase
        public static void StandardizeUrn(AnnotationList anList)
        {
            foreach (Annotation a in anList)
            {
                if ((a.Type == "ARTEFACT_URN" || a.Type == "EXCLUDES") && 
                    a.Url != null)
                {
                    string text = a.Url.ToString();
                    text = text.Replace(".codelist=", ".codelist.CodeList=");
                    text = text.Replace(".hierarchicalcodelist=", ".hierarchicalcodelist.HierarchicalCodelist=");
                    text = text.Replace(".code=", ".Code=");
                    a.Url = new Uri(text);
                }
            }
        }


    }
}
